﻿
using System;
using System.Collections.Generic;
using System.Linq;
using WPFSchematics.Definition.Common;

namespace WPFSchematics.Graphics.Ghosts
{
    /// <summary>
    /// 幻影事务管理器。
    /// </summary>
    public class GhostTranscation:ITranscation,IGhostRestore
    {
        /// <summary>
        /// 幻影集合。
        /// </summary>
        List<IGhostRestore> _restoreList;

        /// <summary>
        /// 宿主集合。
        /// </summary>
        List<IGhostHost> _hostList;

        /// <summary>
        /// 
        /// </summary>
        int _nextUndo;

        /// <summary>
        /// This event is raised after any operation which can change
        /// UndoManager state. Client can subscribe to this event and
        /// check CanUndo, CanRedo and IsDirty values.
        /// </summary>
        public event EventHandler StateChanged;
        
        /// <summary>
        /// 
        /// </summary>
        private GhostTranscation()
        {
            Reset();
        }

        /// <summary>
        /// 
        /// </summary>
        static GhostTranscation()
        {
            Instance = new GhostTranscation();
        }

        /// <summary>
        /// 
        /// </summary>
        public static GhostTranscation Instance { get; private set; }

        /// <summary>
        /// Return true if Undo operation is available
        /// </summary>
        public bool CanUndo
        {
            get
            {
                // If the NextUndo pointer is -1, no commands to undo
                return _nextUndo >= 0 && _nextUndo <= _restoreList.Count - 1;
            }
        }

        /// <summary>
        /// Return true if Redo operation is available
        /// </summary>
        public bool CanRedo
        {
            get
            {
                // If the NextUndo pointer points to the last item, no commands to redo
                return _nextUndo != _restoreList.Count - 1;
            }
        }

        /// <summary>
        /// Returns dirty flag for client document.
        /// Document is dirty if it is possible to make Undo operation -
        /// I hope this is correct.
        /// 
        /// This can be changed if history has restricted length.
        /// Once history is trimmed from the beginning, IsDirty should
        /// be always true.
        /// </summary>
        public bool IsDirty
        {
            get
            {
                return CanUndo;
            }
        }

        /// <summary>
        /// Clear History
        /// </summary>
        public void ClearHistory()
        {
            _restoreList = new List<IGhostRestore>();
            _nextUndo = -1;
            RaiseStateChangedEvent();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Reset()
        {
            _hostList = new List<IGhostHost>();
            ClearHistory();
        }

        /// <summary>
        /// Add new command to history.
        /// Called by client after executing some restore.
        /// </summary>
        /// <param name="restore"></param>
        public void AddActionToHistory(IGhostRestore restore)
        {
            // Purge history list
            TrimHistoryList();

            // Add command and increment undo counter
            _restoreList.Add(restore);

            _nextUndo++;

            RaiseStateChangedEvent();
        }

        /// <summary>
        /// Undo
        /// </summary>
        public void Undo()
        {
            if (!CanUndo)
            {
                return;
            }
            var action = _restoreList[_nextUndo];
            action.Undo();
            _nextUndo--;
            RaiseStateChangedEvent();
        }

        /// <summary>
        /// Redo
        /// </summary>
        public void Redo()
        {
            if (!CanRedo)
            {
                return;
            }
            var itemToRedo = _nextUndo + 1;
            var action = _restoreList[itemToRedo];
            action.Redo();
            _nextUndo++;

            RaiseStateChangedEvent();
        }

        /// <summary>
        /// 
        /// </summary>
        private void TrimHistoryList()
        {
            // We can redo any undone command until we execute a new 
            // command. The new command takes us off in a new direction,
            // which means we can no longer redo previously undone actions. 
            // So, we purge all undone commands from the history list.*/

            // Exit if no items in History list
            if (_restoreList.Count == 0)
            {
                return;
            }

            // Exit if NextUndo points to last item on the list
            if (_nextUndo == _restoreList.Count - 1)
            {
                return;
            }

            // Purge all items below the NextUndo pointer
            for (var i = _restoreList.Count - 1; i > _nextUndo; i--)
            {
                _restoreList.RemoveAt(i);
            }
        }

        /// <summary>
        /// Raise UndoManagerOperation event.
        /// </summary>
        private void RaiseStateChangedEvent()
        {
            if (StateChanged != null)
            {
                StateChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ghostHost"></param>
        public void RegisteGhostHost(IGhostHost ghostHost)
        {
            //ghostHost.Observer.GhostProperties.Clear();
            //ghostHost.RegisterGhostProperty();
            _hostList.Add(ghostHost);
        }

        /// <summary>
        /// 启动状态监视。
        /// </summary>
        public void BeginTranscation()
        {
            if (_hostList == null) return;
            foreach (var host in _hostList)
            {
                host.Observer.BeginSnapGhost();
            }
        }

        /// <summary>
        /// 停止状态监视。
        /// </summary>
        public void EndTranscation()
        {
            if (_hostList == null) return;
            foreach (var ghost in _hostList.Select(host => host.Observer.EndSnapGhost()).Where(ghost => ghost != null))
            {
                Instance.AddActionToHistory(new GhostRestore(ghost));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public int Index
        {
            get { return -1; }
        }
    }
}
